module necrophilia.animation.Instance;
import necrophilia.animation.Base;
import necrophilia.Time;
import maths.CoordSys;



/** 
	Each model has it's own animation set. Even for models that are useing this same animations
	each model is in different state - different point of the timeline. For example one model is starting
	the walking loop, when the other is in the middle. But giveing each odel animation is not necessery
	The only thing differing theas animations per model is the mencioned timeline. Thats why 
	Animation Instance is created.  Animation Instance is Animation + it's timeline for particular model.
	So model only get's his AnimationInstance. The timeline here is implemented by bricks.Time.Timer;
	This implementation of Animation Instance is a template, where you can set the type of the Timer.
	Any Timer can be used if it implements things that bricks.Time.Timer doas. 
*/
class AnimationInstance(Tim){
	alias Tim.TimeType Time; /// alias for timer Time type to keep the types
	alias Tim TimerType;
	protected Animation!(Time) _animation;
	protected Tim _timer;
	
	/** 
		Constructor
		Params:
		animation= animation that is represented by Animation Instance
	*/
	this(Animation!(Time) animation){
		_animation=animation;
		_timer= new Tim(animation.speed, animation.start);
	}
	
	static if(is(Tim : LoopTimer!(Time))){
		/** 
			When using LoopTimer as a timer, there is possibility to set min and max in AnimationInstance Constructior
			if not, remember to set borders for the timer before first updating the AnimationInstance
		*/
		this(Animation!(Time) animation, Time min, Time max){
			_animation=animation;
			_timer= new Tim(animation.speed, animation.start, min, max);
		}
	}
	
	/**
		Updating the Animation Instance. 
	*/
	void update(){
		_timer.update();
	}
	
	void getFrameForBone(int boneIndex, int offset=0){
		
	}
	
	/** 
		pass-thru methods to animation keyframes set see description in bricks.Animation.Animation.
		Usage same as for bricks.Time above
		So you get informations like from Animation itself but by working on your Animation Instance, what is very confortable.
		Everything you do to the animation instance is complitlly safe for other models.
	*/
	Keyframe!(Time)[] keyframes(int boneIndex){
		return _animation.keyframes(boneIndex);
	}
	
	/// ditto
	Time start(){
		return _animation.start;
	}
	
	/// ditto
	int bonesNumber(){
		return _animation.bonesNumber;
	}
	
	/// ditto
	char[] name(){
		return _animation.name;
	}
		
	static if(is(Tim : Timer!(Time))){
		/** 
			pass-thru methods to command animations timer if it is from type bricks.Time 
			see description in bricks.Time
			Examples:
			-----------
				// if you want to pause animation instance just do
				// lets say anim is some Animation;
				AnimationInstance(Timer) foo=new AnimationInstance(anim);
				foo.pause; 
			-----------
			So you can use all of this methods as if you were useing Timer methods form bricks.Time
		*/
		void pause() { _timer.pause(); }
		/// ditto
		void playForward() { _timer.playForward(); } 
		/// ditto
		void playBackward() { _timer.playBackward(); } 
		/// ditto
		bool isPaused() { return _timer.isPaused(); } 
		/// ditto
		bool isPlayingForward() { return _timer.isPlayingForward(); } 
		/// ditto
		bool isPlayingBackward() {return _timer.isPlayingBackward(); } 
		/// ditto
		Time time(){return _timer.time(); } 
		/// ditto
		Time setTime(Time time){ return _timer.setTime(time); } 
		/// ditto
		Time speed(){ return _timer.speed(); } 
		/// ditto
		Time setSpeed(Time speed){ return _timer.setSpeed(time); } 
	}
	
	static if(is(Tim : LoopTimer!(Time))){
		/** 
			pass-thru methods to command animations timer if it is from type bricks.LoopTime 
			see description in bricks.LoopTime
			Examples:
			-----------
				// if you want to pause animation instance just do
				// lets say anim is some Animation;
				AnimationInstance(LoopTimer) foo=new AnimationInstance(anim);
				if(foo.isStoped) doSomething; 
			-----------
			So you can use all of this methods as if you were useing Timer methods form bricks.LoopTime
		*/
		void setNewBorders(Time min, Time max){ _timer.setNewBorders(min,max); }
		/// ditto
		bool isStoped() { return  _timer.isStoped; }
		/// ditto
		bool isPlayingForwardLoop() { return _timer. isPlayingForwardLoop(); }
		/// ditto
		bool isPlayingBackwardLoop() { return _timer. isPlayingBackwardLoop(); } 
		/// ditto
		void playForwardLoop() {  _timer.playForwardLoop(); }
		/// ditto
		void playBackwardLoop() { _timer.playBackwardLoop(); }
		///ditto
		Time min(){ return _timer.min; }
		///ditto
		Time max(){ return _timer.max;	}
	}
}

unittest {
	// for normal Animation instance
	{
		Animation!(float) animation=new Animation!(float)(1, 0.01, 0);
		animation.addKeyframe(0,0.4,CoordSys.identity);
		AnimationInstance!(Timer!(float) ) anim=new AnimationInstance!(Timer!(float))(animation);
		anim.update;
		assert(anim.isPaused==false);
		anim.pause;
		assert(anim.isPaused==true);
		assert(anim.bonesNumber==1);
		assert(anim.time==0.01f);
		assert(anim.keyframes(0)[0].time==0.4f);
	}
	
	// for Animation Instance with LoopTimer
	{
		Animation!(float) animation=new Animation!(float)(1, 0.01, 0);
		animation.addKeyframe(0,0.4,CoordSys.identity);
		AnimationInstance!(LoopTimer!(float) ) anim=new AnimationInstance!(LoopTimer!(float))(animation);
		{
			bool exCatched=false;
			try {
				anim.update;
			} catch (Exception e){
				exCatched=true;
			}
			assert(exCatched);
			anim.setNewBorders(0,1);
			anim.update;
		}
		assert(anim.isPaused==false);
		anim.pause;
		assert(anim.isPaused==true);
		assert(anim.bonesNumber==1);
		assert(anim.time==0.01f);
		assert(anim.keyframes(0)[0].time==0.4f);
		assert(anim.isStoped==false);
	} 
	

}